package com.ibeeking.found.msg.rest.service.impl;

import com.ibeeking.found.common.constants.WxMpConstant;
import com.ibeeking.found.common.utils.UserUtils;
import com.ibeeking.found.msg.rest.service.*;
import com.ibeeking.found.msg.service.common.annotation.MessageType;
import com.ibeeking.found.msg.service.common.constants.RedisKeyConstant;
import com.ibeeking.found.msg.service.common.dos.WxMpFansDO;
import com.ibeeking.found.msg.service.common.dos.WxMpGroupMessageCountDO;
import com.ibeeking.found.msg.service.common.dos.WxMpGroupMessageDO;
import com.ibeeking.found.msg.service.common.dos.WxMpGroupMessageObjectDO;
import com.ibeeking.found.msg.service.common.param.WxMpGroupMessageParam;
import com.ibeeking.found.msg.service.common.vo.WxMpSourceDetailVO;
import com.ibeeking.found.msg.service.common.vo.WxMpSourceVO;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.log.utils.LogUtils;
import com.ibeeking.nematos.redis.utils.RedisUtils;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.StringUtils;

import com.alibaba.fastjson.JSONObject;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.WxMpTemplateMsgService;
import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult;
import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import me.chanjar.weixin.mp.enums.WxMpApiUrl;
import me.chanjar.weixin.open.api.WxOpenMpService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName MsgServiceImpl
 * @Description
 * @Author ibeeking
 * @Date 2021-02-06 16:15
 **/

@Service
public class MsgServiceImpl implements IMsgService {

    @Resource
    private IOpenService wxOpenServiceImpl;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private IWxMpFansService mpFansService;
    @Resource
    private IWxMpGroupMessageCountService wxmpGroupMessageCountService;

    @Resource
    private IWxMpSourceService sourceService;

    @Resource
    private IWxMpGroupMessageObjectService wxmpGroupMessageObjectService;


    /**
     * 异步群发消息
     *
     * @param fans
     * @param param
     * @param wxMpMassUploadResult
     * @param key
     */
    @Override
    //@Async
    @Transactional(rollbackFor = Exception.class)
    public void sendMessageByOpenId(List<WxMpFansDO> fans, WxMpGroupMessageParam param, WxMpMassUploadResult wxMpMassUploadResult, WxMpGroupMessageDO wxmpGroupMessageDO, String key) {

        if (Collections3.isEmpty(fans)) {
            return;
        }

        //微信端口限制每分钟限制60次,每日100次
        int size = fans.size();
        int batch = 10000;
        int pages = (size - 1) / batch + 1;
        int min = 0;
        //发送的对象
        String jsonData = null;
        String lockKey = RedisKeyConstant.MODULE + "lockKey";
        for (int i = 0; i < pages; i++) {
            WxMpMassSendResult result = null;
            List<String> sendOpendIds = null;
            //获取发送的openId
            List<WxMpFansDO> groupFans = fans.stream().skip(min).limit(batch).collect(Collectors.toList());
            sendOpendIds = groupFans.stream().map(f -> f.getOpenId()).collect(Collectors.toList());
            min = min + batch;
            try {
                //获取锁
                boolean lock = false;
                while (lock == false) {
                    lock = this.redisUtils.tryLock(lockKey, 5, 40);
                }
                if (lock) {
                    //获取当前值
                    Integer count = this.getCount(key);
                    //如果发送次数大于100次 结束后面的发送任务
                    if (count >= 100) {
                        return;
                    }
                    //向公众号发送
                    if (WxMpConstant.MESSAGE_VIDEO.equals(param.getMessageTypeString())) {
                        param.setMediaId(wxMpMassUploadResult.getMediaId());
                    }
                    jsonData = this.getJsonString(param, sendOpendIds);
                    result = this.massMessageSend(param.getAppId(), jsonData, false);

                    //发送完毕后向缓存中增加一次本日发送的次数
                    if (result != null) {
                        count = count + 1;
                        redisUtils.set(key, count, RedisKeyConstant.DAY);
                    } else {
                        throw new BusinessException("网络异常");
                    }
                } else {
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
                //出现异常只捕获，不抛出
                continue;
            } finally {
                //释放锁
                try {
                    redisUtils.unlock(lockKey);
                    wait(RedisKeyConstant.SECOND);
                } catch (Exception e) {
                    LogUtils.error("释放锁异常:{}", e);
                    e.printStackTrace();
                }
            }
            //记录发布数据
            this.saveSendInfo(i, result, wxmpGroupMessageDO, param.getMessageType(), groupFans);
        }

    }

    /**
     * 获取键值
     *
     * @param key
     * @return
     */
    @Override
    public Integer getCount(String key) {
        Object o = this.redisUtils.get(key);
        Integer count = 0;
        if (o == null) {
            return count;
        }
        String value = String.valueOf(this.redisUtils.get(key));
        if (StringUtils.isNotBlank(value)) {
            try {
                count = Integer.valueOf(value);
            } catch (Exception e) {
                e.printStackTrace();
                throw new BusinessException("发送失败");
            }
        }
        return count;
    }

    /**
     * 使用反射获取方法上的注解,
     * 根据value值进行选择使用
     *
     * @param param
     * @param ids
     * @return
     */
    @Override
    public String getJsonString(WxMpGroupMessageParam param, List<String> ids) {
        //获取所有方法
        Method[] methods = this.getClass().getMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(MessageType.class)) {
                MessageType messageType = method.getAnnotation(MessageType.class);
                if (messageType.value().equals(param.getMessageTypeString())) {
                    try {
                        return (String) method.invoke(this, param, ids);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

    /**
     * 发送请求
     *
     * @param appId
     * @param json
     * @return
     */
    @Override
    public WxMpMassSendResult massMessageSend(String appId, String json, boolean allSend) {
        Long tenantId = UserUtils.getTenantID();
        WxOpenMpService wxOpenMpService = wxOpenServiceImpl.getWxOpenMpService(tenantId);
        WxMpTemplateMsgService wxMpTemplateMsgService = wxOpenMpService.getTemplateMsgService();
        WxMpTemplateMessage message = new WxMpTemplateMessage();
        String result = null;
        try {
            if (allSend) {
                result = wxOpenMpService.post(WxMpApiUrl.MassMessage.MESSAGE_MASS_SENDALL_URL, json);
            } else {
                result = wxOpenMpService.post(WxMpApiUrl.MassMessage.MESSAGE_MASS_SEND_URL, json);
            }
            Map<String, Object> map = JSONObject.parseObject(result, HashMap.class);
            return getSendResult(map);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(e.getMessage());
        }

    }

    @Override
    public WxMpMassSendResult getSendResult(Map map) {

        String msgId = String.valueOf(map.get("msg_id"));
        String msgDataId = String.valueOf(map.get("msg_data_id"));

        WxMpMassSendResult result = new WxMpMassSendResult();
        result.setMsgDataId(msgDataId);
        result.setMsgId(msgId);
        result.setErrorCode(String.valueOf(map.get("errcode")));
        result.setErrorMsg(String.valueOf(map.get("errmsg")));
        return result;

    }

    @Override
    public void saveSendInfo(int index, WxMpMassSendResult result, WxMpGroupMessageDO wxmpGroupMessageDO, Integer messageType, List<WxMpFansDO> fans) {
        //记录发布数据
        //1.记录发送数据
        WxMpGroupMessageCountDO wxmpGroupMessageCountDO = new WxMpGroupMessageCountDO();
        wxmpGroupMessageCountDO.setSort(index);
        wxmpGroupMessageCountDO.setMsgDataId(result.getMsgDataId());
        wxmpGroupMessageCountDO.setMsgId(result.getMsgId());
        wxmpGroupMessageCountDO.setGroupMessageId(wxmpGroupMessageDO.getId());
        wxmpGroupMessageCountDO.setCreateTime(LocalDateTime.now());
        wxmpGroupMessageCountDO.setModifyTime(LocalDateTime.now());
        this.wxmpGroupMessageCountService.add(wxmpGroupMessageCountDO);
        //2.记录发送的人
        if (Collections3.isNotEmpty(fans)) {
            List<WxMpGroupMessageObjectDO> odos = new ArrayList<>();
            for (WxMpFansDO fansDO : fans) {
                WxMpGroupMessageObjectDO odo = new WxMpGroupMessageObjectDO();
                odo.setGroupMessageId(wxmpGroupMessageDO.getId());
                odo.setMessageType(messageType);
                odo.setOpenId(fansDO.getOpenId());
                odo.setAppId(wxmpGroupMessageDO.getAppId());
                odo.setCreateTime(LocalDateTime.now());

                odos.add(odo);
            }
            this.wxmpGroupMessageObjectService.insertBatchSomeColumn(odos);
        }
        //3.更新发送人本月发送次数
        if (Collections3.isNotEmpty(fans)) {
            //根据id批量更新
            this.mpFansService.updateFansSendCountByIdsBatch(fans, 1000);
        } else {
            //全部发送式更新
            this.mpFansService.updateAllFansSendCount(wxmpGroupMessageDO.getAppId());
        }

    }

    @Override
    public String newTypeStr2OldTypeStr(String newType) {

        if (newType == null) {
            return null;
        }

        switch (newType) {

            case "TEXT":
                return WxMpConstant.MESSAGE_TEXT;
            case "PIC":
                return WxMpConstant.MESSAGE_IMAGE;
            case "VOICE":
                return WxMpConstant.MESSAGE_VOICE;
            case "VIDEO":
                return WxMpConstant.MESSAGE_VIDEO;
            case "IMAGE_TEXT":
                return WxMpConstant.MESSAGE_NEWS;
            default:
                return null;

        }


    }


    @Override
    public int newType2OldType(String newType) {

        if (newType == null) {
            return 0;
        }

        switch (newType) {

            case "TEXT":
                return 5;
            case "PIC":
                return 2;
            case "VOICE":
                return 3;
            case "VIDEO":
                return 4;
            case "IMAGE_TEXT":
                return 1;
            default:
                return 0;

        }


    }

    @Override
    public String OldType2NewType(Integer newType) {

        if (newType == null) {
            return null;
        }

        switch (newType) {

            case 5:
                return "TEXT";
            case 2:
                return "PIC";
            case 3:
                return "VOICE";
            case 4:
                return "VIDEO";
            case 1:
                return "IMAGE_TEXT";
            default:
                return null;

        }


    }

    /**
     * 发送文本消息转换为文本所需的json
     *
     * @param openIds
     * @param param
     * @return
     */
    @MessageType(value = "text")
    public String getTextJsonString(WxMpGroupMessageParam param, List<String> openIds) {
        Map<String, Object> map = new HashMap<>();

        Map<String, Object> filter = new HashMap<>();
        if (param.getAllSend() == 1) {
            filter.put("is_to_all", true);
            map.put("filter", filter);
        } else {
            map.put("touser", openIds);
        }
        Map<String, Object> content = new HashMap<>();
        content.put("content", param.getContent());
        map.put("text", content);
        map.put("msgtype", "text");
        return JSONObject.toJSONString(map);
    }

    /**
     * 发送音频消息转换为音频所需的json
     *
     * @param openIds
     * @param param
     * @return
     */
    @MessageType(value = "voice")
    public String getVoiceJsonString(WxMpGroupMessageParam param, List<String> openIds) {
        Map<String, Object> map = new HashMap<>();

        Map<String, Object> filter = new HashMap<>();
        if (param.getAllSend() == 1) {
            filter.put("is_to_all", true);
            map.put("filter", filter);
        } else {
            map.put("touser", openIds);
        }
        Map<String, Object> voice = new HashMap<>();
        voice.put("media_id", param.getMediaId());
        map.put("voice", voice);
        map.put("msgtype", "voice");
        return JSONObject.toJSONString(map);
    }

    /**
     * 发送图片消息转换为图片所需的json
     *
     * @param openIds
     * @param param
     * @return
     */
    @MessageType(value = "image")
    public String getImageJsonString(WxMpGroupMessageParam param, List<String> openIds) {
        List<String> localMedias = Arrays.asList(StringUtils.split(param.getMediaId(), ","));
        List<String> medias = localMedias.stream().map(id ->
                this.sourceService.getTempMedia(Long.valueOf(id), param.getMessageTypeString(), param.getAppId()).getMediaId()
        ).collect(Collectors.toList());
        Map<String, Object> map = new HashMap<>();
        Map<String, Object> filter = new HashMap<>();
        if (param.getAllSend() == 1) {
            filter.put("is_to_all", true);
            map.put("filter", filter);
        } else {
            map.put("touser", openIds);
        }
        Map<String, Object> images = new HashMap<>();
        images.put("media_ids", medias);
        images.put("recommend", param.getRecommend());
        images.put("need_open_comment", param.getNeedOpenComment());
        images.put("only_fans_can_comment", param.getOnlyFansCanComment());
        map.put("images", images);
        map.put("msgtype", "images");
        return JSONObject.toJSONString(map);

    }

    @MessageType(value = "video")
    public String getVideoJson(WxMpGroupMessageParam param, List<String> openIds) {
        Map<String, Object> map = new HashMap<>();
        Map<String, Object> filter = new HashMap<>();
        if (param.getAllSend() == 1) {
            filter.put("is_to_all", true);
            map.put("filter", filter);
        } else {
            map.put("touser", openIds);
        }
        map.put("filter", filter);
        Map<String, Object> mpvideo = new HashMap<>();
        mpvideo.put("media_id", param.getMediaId());
        map.put("mpvideo", mpvideo);
        map.put("msgtype", WxConsts.MassMsgType.MPVIDEO);
        return JSONObject.toJSONString(map);
    }

    @MessageType(value = "news")
    public String getNewsJson(WxMpGroupMessageParam param, List<String> openIds) {

        WxMpSourceVO newsList = sourceService.view(Long.valueOf(param.getMediaId()));
        List<Map<String, Object>> articles = new ArrayList<>();
        Map<String, List<Map<String, Object>>> obj = new HashMap<>();
        if (newsList == null || Collections3.isEmpty(newsList.getSourceDetail())) {
            return JSONObject.toJSONString(obj);
        }

        for (WxMpSourceDetailVO news : newsList.getSourceDetail()) {
            Map<String, Object> map = new HashMap<>();
            WxMediaUploadResult result = sourceService.getTempMedia(news.getThumbMediaId(), WxConsts.MediaFileType.THUMB, param.getAppId());
            map.put("thumb_media_id", result.getThumbMediaId());
            map.put("author", news.getAuthor());
            map.put("title", news.getTitle());
            map.put("content_source_url", news.getContentSourceUrl());
            map.put("content", news.getContent());
            map.put("digest", news.getDigest());
            map.put("show_cover_pic", news.getShowCoverPic());
            map.put("need_open_comment", news.getNeedOpenComment());
            map.put("only_fans_can_comment", news.getOnlyFansCanComment());
            articles.add(map);
        }
        obj.put("articles", articles);
        String json = JSONObject.toJSONString(obj);
        Long tenantId = UserUtils.getTenantID();
        WxMpService wxMpService = wxOpenServiceImpl.getWxOpenMpService(tenantId);
        try {
            String result = wxMpService.post(WxMpApiUrl.MassMessage.MEDIA_UPLOAD_NEWS_URL, json);
            WxMpMassUploadResult upload = WxMpMassUploadResult.fromJson(result);
            Map<String, Object> map = new HashMap<>();
            Map<String, Object> filter = new HashMap<>();
            if (param.getAllSend() == 1) {
                filter.put("is_to_all", true);
                map.put("filter", filter);
            } else {
                map.put("touser", openIds);
            }
            Map<String, Object> mpnews = new HashMap<>();
            mpnews.put("media_id", upload.getMediaId());
            map.put("mpnews", mpnews);
            map.put("msgtype", WxConsts.MassMsgType.MPNEWS);
            map.put("send_ignore_reprint", param.getSendIgnoreReprintl());
            return JSONObject.toJSONString(map);
        } catch (Exception e) {
            LogUtils.error("上传图文素材异常:{}", e);
        }
        return "";
    }
}
